﻿// (c) Copyright Damian Mehers http://damianblog.com/
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using MceFM.Last.FM;

namespace MceFM.Last {
  internal class Downloader  {
    private readonly List<Track> tracks;
    private Track currentTrack;
    private int currentTrackIndex = -1;
    private Track skipToTrack;
    private readonly object tracksMonitor = new object();
    private readonly UserNotifier userNotifier;
    private LastFm lastFm;

    public LastFm LastFm {
      get { return lastFm;  }
      set { lastFm = value; }
    }

    public void Start() {
      Thread thread = new Thread(ThreadEntry) {IsBackground = true};
      thread.Start();
    }

    public Downloader(UserNotifier userNotifier, List<Track> tracks) {
      this.tracks = tracks;
      this.userNotifier = userNotifier;
    }

    public void AddedTrack() {
      lock(tracksMonitor) {
        Monitor.Pulse(tracksMonitor);
      }
    }

    public void SkipTo(Track skipToTrack) {
      lock(tracksMonitor) {
        if(skipToTrack == currentTrack) {
          return;
        }
        this.skipToTrack = skipToTrack;
        Trace.TraceInformation("Skipping to {0}", skipToTrack);
      }
    }

    private void ThreadEntry() {
      try {
        while(true) {
          currentTrack = NextTrack();
          if(currentTrack.DownloadError) {
            Trace.TraceError("Not downloading {0} because it already has had a download error", currentTrack);
            continue;
          }

          if(currentTrack.Content != null) {
            Trace.TraceError("Not downloading {0} because it already downloaded", currentTrack);
            continue;
          }

          using(WebClient webClient = new WebClient()) {
            webClient.Headers[HttpRequestHeader.UserAgent] = "mfm";
            webClient.Headers[HttpRequestHeader.Cookie] = "Session = " + lastFm.Session;
            int pos = 0;

            try {
              Trace.TraceInformation("Downloader: Opening {0} for {1}", currentTrack.Location, currentTrack.Title);
              Stream lastFmResponseStream = webClient.OpenRead(currentTrack.Location);
              int lastFmContentLength = int.Parse(webClient.ResponseHeaders[HttpResponseHeader.ContentLength]);
              Trace.TraceInformation("Downloader: Content length is {0}", lastFmContentLength);

              byte[] content = new byte[lastFmContentLength];
              currentTrack.Content = content;

              for(int bytesRead = -1;bytesRead != 0 && skipToTrack == null;) {
                bytesRead = lastFmResponseStream.Read(content, pos, content.Length - pos);
                pos += bytesRead;
                currentTrack.ContentLength = pos;
//                Trace.TraceInformation("Downloader: Downloaded {0}/{1} of {2}", pos, content.Length, currentTrack.Title);
              }
              Trace.TraceInformation("skipToTrack is {0}", skipToTrack);
              if(skipToTrack != null) {
                currentTrack.DownloadError = true;
                currentTrack.ContentLength = 0;
              }
              Trace.TraceInformation("Downloader: Finished {0}", currentTrack.Title);
              lastFmResponseStream.Close();
            } catch(Exception ex) {
              Trace.TraceError("Downloader: Error downloading {0} from {1}: {2}",
                               currentTrack.Title,
                               currentTrack.Location,
                               ex);
              currentTrack.DownloadError = true;
              currentTrack.ContentLength = 0;
            }
          }
        }
      } catch(Exception ex) {
        Trace.TraceError("Downloader had unexpected error: {0}", ex);
        userNotifier.Notify("Downloader had unexpected error:" + ex);
      }
    }

    private Track NextTrack() {
      Track localSkipToTrack;
      Trace.TraceInformation("Downloader: Getting next track.");

      lock(tracksMonitor) {
        localSkipToTrack = skipToTrack;
        skipToTrack = null;
      }

      if(localSkipToTrack != null) {
        currentTrackIndex = -1;
        for(int i = 0;i < tracks.Count;i++) {
          if(!tracks[i].Equals(localSkipToTrack)) {
            continue;
          }
          currentTrackIndex = i;
          break;
        }
        if(currentTrackIndex == -1) {
          Trace.TraceInformation("Cannot find {0}", localSkipToTrack);
        }
        Trace.TraceInformation("Downloader skipping to {0} at index {1}", localSkipToTrack, currentTrackIndex);
        return localSkipToTrack;
      }


      lock(tracksMonitor) {
        while(currentTrackIndex == tracks.Count - 1) {
          Monitor.Wait(tracksMonitor);
        }
      }

      return tracks[++currentTrackIndex];
    }
  }
}